﻿#include "simple_client.h"
#include "include/cef_app.h"
#include "include/views/cef_box_layout.h"
#include "include/views/cef_fill_layout.h"
#include "include/wrapper/cef_helpers.h"

const auto DEFAULT_BKG_COLOR = CefColorSetARGB(255, 255, 245, 247);

static std::list<CefRefPtr<CefWindow>> _window_list;

SimpleClient::SimpleClient()
{
    LOG(INFO) << "CCefSimpleClient()";
}

SimpleClient::~SimpleClient()
{
    LOG(INFO) << "~CCefSimpleClient()";
}

void SimpleClient::CreateMainWindow()
{
    CefRefPtr<SimpleClient> client(new SimpleClient);
    client->main_window_ = CefWindow::CreateTopLevelWindow(client.get());

    CefBoxLayoutSettings settings;
    settings.default_flex = 100;
    client->main_window_->SetToBoxLayout(settings);

    client->url_text_field_ = CefTextfield::CreateTextfield(nullptr);
    client->url_text_field_->SetBackgroundColor(DEFAULT_BKG_COLOR);
    client->main_window_->AddChildView(client->url_text_field_);

    client->browser_view_panel_ = CefPanel::CreatePanel(nullptr);
    client->browser_view_panel_->SetToFillLayout();
    client->browser_view_panel_->SetBackgroundColor(DEFAULT_BKG_COLOR);
    client->main_window_->AddChildView(client->browser_view_panel_);

    client->main_window_->Show();
    client->main_window_->CenterWindow(CefSize(1176, 742));
    client->ShowDefaultPage();
}

void SimpleClient::OnBeforeContextMenu(CefRefPtr<CefBrowser> browser,
                                       CefRefPtr<CefFrame> frame,
                                       CefRefPtr<CefContextMenuParams> params,
                                       CefRefPtr<CefMenuModel> model)
{
    CEF_REQUIRE_UI_THREAD();
    ALLOW_UNUSED_LOCAL(browser);
    ALLOW_UNUSED_LOCAL(frame);
    ALLOW_UNUSED_LOCAL(params);
    ALLOW_UNUSED_LOCAL(model);
    model->Clear();
}

void SimpleClient::OnAddressChange(CefRefPtr<CefBrowser> browser,
                                   CefRefPtr<CefFrame> frame,
                                   const CefString &url)
{
    CEF_REQUIRE_UI_THREAD();
    ALLOW_UNUSED_LOCAL(browser);
    ALLOW_UNUSED_LOCAL(frame);

    url_text_field_->SetText(url);
}

void SimpleClient::OnTitleChange(CefRefPtr<CefBrowser> browser,
                                 const CefString &title)
{
    CEF_REQUIRE_UI_THREAD();
    ALLOW_UNUSED_LOCAL(browser);

    main_window_->SetTitle(title);
}

void SimpleClient::OnFaviconURLChange(CefRefPtr<CefBrowser> browser,
                                      const std::vector<CefString> &icon_urls)
{
    CEF_REQUIRE_UI_THREAD();
    ALLOW_UNUSED_LOCAL(browser);
    ALLOW_UNUSED_LOCAL(icon_urls);
}

bool SimpleClient::OnConsoleMessage(CefRefPtr<CefBrowser> browser,
                                    cef_log_severity_t level,
                                    const CefString &message,
                                    const CefString &source,
                                    int line)
{
    CEF_REQUIRE_UI_THREAD();
    ALLOW_UNUSED_LOCAL(browser);
    ALLOW_UNUSED_LOCAL(level);
    ALLOW_UNUSED_LOCAL(message);
    ALLOW_UNUSED_LOCAL(source);
    ALLOW_UNUSED_LOCAL(line);
    return true;
}

void SimpleClient::OnBeforeDownload(CefRefPtr<CefBrowser> browser,
                                    CefRefPtr<CefDownloadItem> download_item,
                                    const CefString &suggested_name,
                                    CefRefPtr<CefBeforeDownloadCallback> callback)
{
    CEF_REQUIRE_UI_THREAD();
    ALLOW_UNUSED_LOCAL(download_item);
    ALLOW_UNUSED_LOCAL(suggested_name);
    LOG(INFO) << "OnBeforeDownload()" << ":browser" << browser->GetIdentifier();
    callback->Continue(CefString(), true);
}

void SimpleClient::OnDownloadUpdated(CefRefPtr<CefBrowser> browser,
                                     CefRefPtr<CefDownloadItem> download_item,
                                     CefRefPtr<CefDownloadItemCallback> callback)
{
    CEF_REQUIRE_UI_THREAD();
    ALLOW_UNUSED_LOCAL(download_item);
    ALLOW_UNUSED_LOCAL(callback);
    LOG(INFO) << "OnDownloadUpdated()" << ":browser" << browser->GetIdentifier();
}

bool SimpleClient::DoClose(CefRefPtr<CefBrowser> browser)
{
    CEF_REQUIRE_UI_THREAD();
    LOG(INFO) << "DoClose()" << ":browser" << browser->GetIdentifier();

    auto browser_view = CefBrowserView::GetForBrowser(browser);
    if (browser_view) {
        RemoveBrowserView(browser_view); //需要移除，否则会导致CefWindow也退出

        auto window = CefWindow::CreateTopLevelWindow(nullptr); //创建一个临时CefWindow
        window->AddChildView(browser_view); //移到到这个新的CefWindow里，否则CefBrowserView无法真正退出
    }
    return false;
}

void SimpleClient::OnBrowserCreated(CefRefPtr<CefBrowserView> browser_view,
                                    CefRefPtr<CefBrowser> browser)
{
    CEF_REQUIRE_UI_THREAD();
    ALLOW_UNUSED_LOCAL(browser_view);
    LOG(INFO) << "OnBrowserCreated()"<< ":browser" << browser->GetIdentifier();
}

void SimpleClient::OnBrowserDestroyed(CefRefPtr<CefBrowserView> browser_view,
                                      CefRefPtr<CefBrowser> browser)
{
    CEF_REQUIRE_UI_THREAD();
    ALLOW_UNUSED_LOCAL(browser_view);
    LOG(INFO) << "OnBrowserDestroyed()" << ":browser" << browser->GetIdentifier();
}

bool SimpleClient::OnPopupBrowserViewCreated(CefRefPtr<CefBrowserView> browser_view,
                                             CefRefPtr<CefBrowserView> popup_browser_view,
                                             bool is_devtools)
{
    CEF_REQUIRE_UI_THREAD();
    ALLOW_UNUSED_LOCAL(browser_view);
    ALLOW_UNUSED_LOCAL(is_devtools);
    LOG(INFO) << "OnPopupBrowserViewCreated()";

    if (is_devtools) { //dev tools 在退出时会自动关闭关联的CefWindow窗口，所以这里需要创建一个独立的CefWindow窗口
        auto window = CefWindow::CreateTopLevelWindow(
                    (CefWindowDelegate*)(main_window_->GetDelegate().get()));
        window->AddChildView(popup_browser_view);
        window->SetPosition(main_window_->GetPosition());
        window->SetSize(main_window_->GetSize());
        window->SetTitle(url_text_field_->GetText());
        window->Show();
    } else {
        AppendBrowserView(popup_browser_view);
    }
    return true;
}

bool SimpleClient::OnKeyEvent(CefRefPtr<CefWindow> window,
                              const CefKeyEvent &event)
{
    CEF_REQUIRE_UI_THREAD();
    ALLOW_UNUSED_LOCAL(window);
    ALLOW_UNUSED_LOCAL(event);
    LOG(INFO) << "OnKeyEvent()";

    if (event.type == KEYEVENT_RAWKEYDOWN) {
        switch (event.windows_key_code) {
        case VK_F5: {
            auto browser_view = GetCurrentBrowserView();
            if (browser_view) {
                auto browser = browser_view->GetBrowser();
                browser->ReloadIgnoreCache();
            }
            break;
        }
        case VK_F12: {
            auto browser_view = GetCurrentBrowserView();
            if (browser_view) {
                auto browser_host = browser_view->GetBrowser()->GetHost();
                if (browser_host->HasDevTools()) {
                    browser_host->CloseDevTools();
                } else {
                    CefWindowInfo window_info; //如果父类使用了CefView则此处window_info参数将不生效
                    browser_host->ShowDevTools(window_info, nullptr, //防止进入SimpleClient::DoClose流程
                                               CefBrowserSettings(), CefPoint());
                }
            }
            break;
        }
        case VK_RETURN: {
            auto browser_view = GetCurrentBrowserView();
            if (browser_view) {
                auto url = url_text_field_->GetText();
                auto frame = browser_view->GetBrowser()->GetMainFrame();
                frame->LoadURL(url);
            }
            break;
        }
        case VK_ESCAPE: {
            auto browser_view = GetCurrentBrowserView();
            if (browser_view) {
                auto browser_host = browser_view->GetBrowser()->GetHost();
                browser_host->CloseBrowser(false); //进入到SimpleClient::DoClose流程
            }
            break;
        }
        default:
            break;
        }
    }
    return false;
}

void SimpleClient::OnWindowCreated(CefRefPtr<CefWindow> window)
{
    CEF_REQUIRE_UI_THREAD();
    LOG(INFO) << "OnWindowCreated()";

    _window_list.push_back(window);
}

void SimpleClient::OnWindowDestroyed(CefRefPtr<CefWindow> window)
{
    CEF_REQUIRE_UI_THREAD();
    LOG(INFO) << "OnWindowDestroyed()";

    _window_list.erase(std::find_if(_window_list.begin(),
                                    _window_list.end(),
                                     [=](CefRefPtr<CefWindow> that) {
        return window->IsSame(that);
    }));

    if (main_window_ && window->IsSame(main_window_)) {
        main_window_ = nullptr;
        url_text_field_ = nullptr;
        browser_view_panel_ = nullptr;
    }

    if (_window_list.size() == 0) {
        CefQuitMessageLoop();
    }
}

void SimpleClient::AppendBrowserView(CefRefPtr<CefBrowserView> browser_view)
{
    browser_view_panel_->AddChildView(browser_view);
    browser_view_list_.push_back(browser_view);
    browser_view->RequestFocus();
}

void SimpleClient::RemoveBrowserView(CefRefPtr<CefBrowserView> browser_view)
{
    browser_view_panel_->RemoveChildView(browser_view);

    std::remove_if(browser_view_list_.rbegin(),
                   browser_view_list_.rend(),
                   [=](CefRefPtr<CefBrowserView> that) {
        return browser_view->IsSame(that);
    });
}

CefRefPtr<CefBrowserView> SimpleClient::GetCurrentBrowserView()
{
    auto find = std::find_if(browser_view_list_.rbegin(),
                             browser_view_list_.rend(),
                             [](CefRefPtr<CefBrowserView> that) {
        return that->IsDrawn();
    });

    if (find != browser_view_list_.rend()) {
        return *find;
    } else {
        return CefRefPtr<CefBrowserView>();
    }
}

void SimpleClient::ShowDefaultPage()
{
    CefBrowserSettings browser_settings;
    browser_settings.image_loading = STATE_DISABLED; //禁止加载图片
    browser_settings.background_color = DEFAULT_BKG_COLOR; //设置背景色
    auto browser_view = CefBrowserView::CreateBrowserView(
                this, CefString("https://www.baidu.com"), browser_settings, nullptr, nullptr, this);
    AppendBrowserView(browser_view);
}
